using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Threading;

using Toenda.Foundation;
using Toenda.Foundation.IO;
using Toenda.Foundation.Utility;

namespace Toenda.Foundation.Windows.Forms {
	/// <summary>
	/// FileCopyForm
	/// </summary>
	public partial class FileCopyForm : Form {
		private FileSystem _fs;

		private string _logfilename;
		
		private bool _progressIsRunning = false;
		private List<FileToCopy> _files;
		private string _currentFile;
		private bool _moveFiles = false;

		private double _sourceFileSize = 0;
		private double _copiedFileSize = 0;

		// ---------------------------------------------------
		// CONSTRUCTORS
		// ---------------------------------------------------

		/// <summary>
		/// Default Ctor
		/// </summary>
		/// <param name="sourceFilename"></param>
		/// <param name="targetFilename"></param>
		/// <param name="moveFile"></param>
		public FileCopyForm(string sourceFilename, string targetFilename, bool moveFile) {
			InitializeComponent();

			this.DialogResult = DialogResult.Cancel;

			this._progressIsRunning = false;
			this._moveFiles = moveFile;

			this.label1.Visible = true;
			this.lblSize.Visible = true;
			
			this._logfilename = Application.StartupPath + @"\Log\logfile.log";
			this._fs = new FileSystem();

			this._fs.FileCopyResponse += new FileSystem.FileCopyEventHandler(_fs_FileCopyResponse);
			this._fs.FileCopyFinish += new FileSystem.FileCopyFinishEventhandler(_fs_FileCopyFinish);

			this._files = new List<FileToCopy>();
			this._files.Add(
				new FileToCopy {
					SourceFilename = sourceFilename, 
					TargetFilename = targetFilename 
				}
			);

			this.Text = "Copy files (0 of " + this._files.Count.ToString() + ")";
		}

		/// <summary>
		/// Default Ctor
		/// </summary>
		/// <param name="files"></param>
		/// <param name="moveFiles"></param>
		public FileCopyForm(List<FileToCopy> files, bool moveFiles) {
			InitializeComponent();

			this.DialogResult = DialogResult.Cancel;

			this._progressIsRunning = false;
			this._moveFiles = moveFiles;

			this.label1.Visible = true;
			this.lblSize.Visible = true;

			this._logfilename = Application.StartupPath + @"\Log\logfile.log";
			this._fs = new FileSystem();

			this._fs.FileCopyResponse += new FileSystem.FileCopyEventHandler(_fs_FileCopyResponse);
			this._fs.FileCopyFinish += new FileSystem.FileCopyFinishEventhandler(_fs_FileCopyFinish);

			this._files = files;

			this.Text = "Copy files (0 of " + this._files.Count.ToString() + ")";
		}

		// ---------------------------------------------------
		// INTERFACE IMPLEMENTATIONS
		// ---------------------------------------------------

		// ---------------------------------------------------
		// PROPERTIES
		// ---------------------------------------------------

		/// <summary>
		/// Get or set the SourceFile
		/// </summary>
		public List<FileToCopy> FilesToCopy {
			get { return this._files; }
			set { this._files = value; }
		}

		// ---------------------------------------------------
		// EVENTS
		// ---------------------------------------------------

		/// <summary>
		/// _fs_FileCopyResponse
		/// </summary>
		/// <param name="args"></param>
		private void _fs_FileCopyResponse(FileCopyEventArgs args) {
			//this.pbCopy.Value += Convert.ToInt32(args.CurrentPackageSize);
			this.ThreadSafe_SetProgressBarStep(Convert.ToInt32(args.CurrentPackageSize));

			//this._currentFile = args.Filename;

			//this.lblFile.Text = args.Text;

			StringBuilder str = new StringBuilder();
			str.Append(Measure.ConvertBytes(
					args.CurrentSize,
					ByteFormat.Kilobytes,
					2
				).ToString()
			);
			str.Append(" KB / ");
			str.Append(Measure.ConvertBytes(
					this._sourceFileSize,
					ByteFormat.Kilobytes,
					2
				).ToString()
			);
			str.Append(" KB");

			this.ThreadSafe_SetSizeText(str.ToString());

			//this.lblSize.Refresh();
			//this.pbCopy.Refresh();
			
			//this.pbCopy.Update();
			//this.Refresh();
			//this.Update();
		}

		/// <summary>
		/// _fs_FileCopyFinish
		/// </summary>
		/// <param name="args"></param>
		private void _fs_FileCopyFinish(FileCopyFinishEventArgs args) {
			if(args.Successfull) {
				this._currentFile = args.Filename;

				this._progressIsRunning = false;

				this.ThreadSafe_SetSizeText(
					Toenda.Foundation.Utility.Text.ShortenString(
						args.Text,
						65,
						StringCutPlaceholderPosition.Middle
					)
				);

				if(this._moveFiles) {
					this._fs.DeleteFile(args.Filename);
				}

				//this.DialogResult = DialogResult.OK;
				//this.Close();

				this.ThreadSafe_WindowClose();
			}
			else {
				this._progressIsRunning = false;

				this.ThreadSafe_SetSizeText(
					Toenda.Foundation.Utility.Text.ShortenString(
						args.Text,
						65,
						StringCutPlaceholderPosition.Ending
					)
				);
			}
		}

		/// <summary>
		/// btnCancel_Click
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void btnCancel_Click(object sender, EventArgs e) {
			if(StaticWindows.Requester(
				"Do you want to delete the already copied files?"
			) == DialogResult.Yes) {
				foreach(FileToCopy file in this._files) {
					try {
						this._fs.DeleteFile(file.TargetFilename);
					}
					catch(Exception) {
					}
				}
			}
			else {
				try {
					this._fs.DeleteFile(this._currentFile);
				}
				catch(Exception) {
				}
			}

			this.DialogResult = DialogResult.Cancel;
			this.Close();
		}

		/// <summary>
		/// FileCopyForm_FormClosing
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void FileCopyForm_FormClosing(object sender, FormClosingEventArgs e) {
			if(this._progressIsRunning) {
				e.Cancel = true;
			}
		}

		/// <summary>
		/// FileCopyForm_Shown
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void FileCopyForm_Shown(object sender, EventArgs e) {
			// TODO: Start copy in own thread
			
			//this._StartCopy();
			
			Thread copyThread = new Thread(new ThreadStart(_StartCopy));
			copyThread.Start();
		}

		// ---------------------------------------------------
		// PRIVATE MEMBERS
		// ---------------------------------------------------

		/// <summary>
		/// Thread-safe control method invoker
		/// </summary>
		/// <param name="size"></param>
		public delegate void SetProgressBarCallback(long size);

		private void ThreadSafe_SetProgressBar(long size) {
			if(this.pbCopy.InvokeRequired) {
				//SetProgressBarCallback d = new SetProgressBarCallback(ThreadSafe_SetProgressBar);
				//this.Invoke(d, new object[] { size });

				this.BeginInvoke(
					new SetProgressBarCallback(ThreadSafe_SetProgressBar),
					new object[] { size }
				);
			}
			else {
				this.pbCopy.Step = 1;
				this.pbCopy.Minimum = 0;
				this.pbCopy.Maximum = Convert.ToInt32(size);
				this.pbCopy.Refresh();
			}
		}

		/// <summary>
		/// Thread-safe control method invoker
		/// </summary>
		/// <param name="stepValue"></param>
		public delegate void SetProgressBarStepCallback(int stepValue);

		private void ThreadSafe_SetProgressBarStep(int stepValue) {
			if(this.pbCopy.InvokeRequired) {
				//SetProgressBarStepCallback d = new SetProgressBarStepCallback(ThreadSafe_SetProgressBarStep);
				//this.Invoke(d, new object[] { stepValue });

				this.BeginInvoke(
					new SetProgressBarStepCallback(ThreadSafe_SetProgressBarStep), 
					new object[] { stepValue }
				);
			}
			else {
				int max = this.pbCopy.Value + stepValue;

				if(max <= this.pbCopy.Maximum) {
					this.pbCopy.Value += stepValue;
					this.pbCopy.Refresh();
				}
				else {
					this.pbCopy.Value = this.pbCopy.Maximum;
					this.pbCopy.Refresh();
				}
			}
		}

		/// <summary>
		/// Thread-safe control method invoker
		/// </summary>
		/// <param name="text"></param>
		public delegate void SetFileTextCallback(string text);

		private void ThreadSafe_SetFileText(string text) {
			if(this.lblFile.InvokeRequired) {
				//SetFileTextCallback d = new SetFileTextCallback(ThreadSafe_SetFileText);
				//this.Invoke(d, new object[] { text });

				this.BeginInvoke(
					new SetFileTextCallback(ThreadSafe_SetFileText),
					new object[] { text }
				);
			}
			else {
				this.lblFile.Text = text;
				this.lblFile.Refresh();
			}
		}

		/// <summary>
		/// Thread-safe control method invoker
		/// </summary>
		/// <param name="text"></param>
		public delegate void SetSizeTextCallback(string text);

		private void ThreadSafe_SetSizeText(string text) {
			if(this.lblSize.InvokeRequired) {
				//SetFileTextCallback d = new SetFileTextCallback(ThreadSafe_SetSizeText);
				//this.Invoke(d, new object[] { text });

				this.BeginInvoke(
					new SetFileTextCallback(ThreadSafe_SetSizeText),
					new object[] { text }
				);
			}
			else {
				this.lblSize.Text = text;
				this.lblSize.Refresh();
			}
		}

		/// <summary>
		/// Thread-safe control method invoker
		/// </summary>
		/// <param name="text"></param>
		public delegate void SetWindowTitleCallback(string text);

		private void ThreadSafe_SetWindowTitle(string text) {
			if(this.lblFile.InvokeRequired) {
				//SetWindowTitleCallback d = new SetWindowTitleCallback(ThreadSafe_SetWindowTitle);
				//this.Invoke(d, new object[] { text });

				this.BeginInvoke(
					new SetWindowTitleCallback(ThreadSafe_SetWindowTitle),
					new object[] { text }
				);
			}
			else {
				this.Text = text;
			}
		}

		/// <summary>
		/// Thread-safe control method invoker
		/// </summary>
		/// <param name="text"></param>
		public delegate void WindowCloseCallback();

		private void ThreadSafe_WindowClose() {
			if(this.lblFile.InvokeRequired) {
				//SetWindowTitleCallback d = new SetWindowTitleCallback(ThreadSafe_SetWindowTitle);
				//this.Invoke(d, new object[] { text });

				this.BeginInvoke(
					new WindowCloseCallback(ThreadSafe_WindowClose)
				);
			}
			else {
				this.DialogResult = DialogResult.OK;
				this.Close();
			}
		}

		/// <summary>
		/// Get the fileindex of a specific file
		/// </summary>
		/// <param name="filename"></param>
		/// <returns></returns>
		private int _GetFileIndex(string filename) {
			string tmp = "";
			int index = 0;

			foreach(FileToCopy file in this._files) {
				index++;

				//tmp = Path.GetFileName(file.SourceFilename);
				tmp = file.SourceFilename;

				if(tmp == filename) {
					return index;
				}
			}

			return -1;
		}

		/// <summary>
		/// Start the delete action
		/// </summary>
		private void _StartCopy() {
			long size = 0;
			int index = 0;

			foreach(FileToCopy file in this._files) {
				size += this._fs.FileSize(file.SourceFilename);
			}

			this.ThreadSafe_SetProgressBar(size);

			this._sourceFileSize = Convert.ToDouble(size);

			foreach(FileToCopy file in this._files) {
				index++;

				FileInfo src = new FileInfo(file.SourceFilename);
				FileInfo trg = new FileInfo(file.TargetFilename);

				this.ThreadSafe_SetFileText(
					Toenda.Foundation.Utility.Text.ShortenString(
						src.Name,
						30,
						StringCutPlaceholderPosition.Middle
					)
					+ " -> "
					+ Toenda.Foundation.Utility.Text.ShortenString(
						trg.Name,
						30,
						StringCutPlaceholderPosition.Middle
					)
				);

				bool overwrite = false;

				if(this._fs.CheckFileExist(file.TargetFilename)) {
					if(StaticWindows.Requester(
						"The file \"" + trg.Name + "\" already exist, do you want to overwrite the file?"
					) == DialogResult.Yes) {
						overwrite = true;
					}
					else {
						overwrite = false;
					}
				}
				else {
					overwrite = true;
				}

				if(overwrite) {
					try {
						this.ThreadSafe_SetWindowTitle(
							"Copy files ("
								+ index
								+ " of "
								+ this._files.Count.ToString()
								+ ")"
						);

						//this.pbCopy.Refresh();
						//this.pbCopy.Update();
						//this.Refresh();
						//this.Update();

						this._progressIsRunning = true;

						DirectoryInfo dir = new DirectoryInfo(
							Path.GetDirectoryName(file.TargetFilename)
						);

						if(!dir.Exists) {
							dir.Create();
						}

						this._fs.CopyFile(
							file.SourceFilename,
							file.TargetFilename,
							750
						);
					}
					catch(Exception) {
						this._progressIsRunning = false;

						StaticWindows.ErrorBox(
							"File Copy Error",
							"The file \"" + trg.Name + "\" is locked and cannot be overwriten!"
						);
					}
				}
				//else {
				//    this.DialogResult = DialogResult.Cancel;
				//    this.Close();
				//}
			}

			this.ThreadSafe_SetProgressBarStep(this.pbCopy.Maximum);
			//this.pbCopy.Value = this.pbCopy.Maximum;
		}

		// ---------------------------------------------------
		// PROTECTED MEMBERS
		// ---------------------------------------------------

		// ---------------------------------------------------
		// PUBLIC MEMBERS
		// ---------------------------------------------------
	}
}